
class_name Protocol

var socket = WebSocketPeer.new()

# 效率不知道高不高，后面需要换成环形buff，整个过程不涉及到内存重分配，性能最佳
var send_buffer = StreamPeerBuffer.new()
var recv_buffer = StreamPeerBuffer.new()

var websocket_url = "ws://192.168.1.3:1234"

var pre_send_time = 0
var ping_delay = 0
var is_connected = false
var check_recv_count = 0
var check_send_count = 0
var heart_beart_interval = 300
var auto_disconnected_frame = 30000

enum EGammaConnectsID
{
	eGC_ShellMsg8			= 0,
	eGC_ShellMsg32			= 253,
	eGC_HeartbeatMsg 		= 254,
	eGC_HeartbeatReply		= 255,
	eGC_CommonEnd			= 256,
};

# Called when the node enters the scene tree for the first time.
func _ready() -> void:
	print("this is ready")
	var err = socket.connect_to_url(websocket_url)
	if err != OK:
		print("Unable to connect")
		is_connected = false
	else:
		is_connected = true
		socket.set_no_delay(true)

# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
	if not is_connected:
		return
		
	socket.poll()
	var state = socket.get_ready_state()
	if state == WebSocketPeer.STATE_OPEN:
		while socket.get_available_packet_count():
			var packet = socket.get_packet()
			if packet.size() > 0:
				recv_buffer.put_data(packet)
				#print("Got data from server: ", socket.get_packet().get_string_from_utf8())
	elif state == WebSocketPeer.STATE_CLOSING:
		pass
	elif state == WebSocketPeer.STATE_CLOSED:
		var code = socket.get_close_code()
		print("WebSocket closed with code: %d. Clean: %s" % [code, code != -1])
		is_connected = false
	
	# 处理发送
	if send_buffer.get_size() > 0:
		_flush_send_buffer()
		
	# 处理接受
	if recv_buffer.get_size() > 0:
		_decode_binary_packet()
	
	_check_time_out()

func _get_header_size(connect_id: EGammaConnectsID):
	if connect_id == EGammaConnectsID.eGC_ShellMsg32:
		return 5
	elif connect_id <  EGammaConnectsID.eGC_ShellMsg32:
		return 2
	else:
		return 1 #u8


func _decode_binary_packet():
	check_recv_count = 0
	while true:
		var buffer_size = recv_buffer.get_size()
		if buffer_size == 0:
			return
		var org_pos = recv_buffer.get_position()
		var msg_type_id = recv_buffer.get_u8()
		var header_size = _get_header_size(msg_type_id)
		if msg_type_id < EGammaConnectsID.eGC_ShellMsg32:
			var msg_len = recv_buffer.get_u8();
			var extra_size = msg_type_id * 256 + msg_len;
			if extra_size + header_size > buffer_size:
				recv_buffer.seek(org_pos)
				return
			var bytes = recv_buffer.get_data(extra_size)
			_on_ShellMsg8(bytes)
		elif msg_type_id == EGammaConnectsID.eGC_ShellMsg32:
			var extra_size = recv_buffer.get_u16()
			if extra_size + header_size  > buffer_size:
				recv_buffer.seek(org_pos)
				return
			var bytes = recv_buffer.get_data(extra_size)
			_on_SheddMsg32(bytes)
		elif msg_type_id == EGammaConnectsID.eGC_HeartbeatMsg:
			_on_heartbeat()
		elif  msg_type_id == EGammaConnectsID.eGC_HeartbeatReply:
			_on_heartbeat_reply()
		else :
			printerr("not define msg")
	
	# 没有处理完的数据重新放到开头
	if recv_buffer.get_size() > 0 and recv_buffer.get_position() > 0:
		var data_bytes = recv_buffer.get_data(recv_buffer.get_available_bytes())
		recv_buffer.seek(0)
		recv_buffer.put_data(data_bytes)
		
	
func _encode_binary_packet(packet: PackedByteArray):
	var total_size = packet.size()
	if total_size < EGammaConnectsID.eGC_ShellMsg32 * 256:
		var id = total_size >> 8;
		var msg_len = total_size & 0xff;
		send_buffer.put_u8(id);
		send_buffer.put_u8(msg_len);
	else:
		var id = EGammaConnectsID.eGC_ShellMsg32;
		var msg_len = packet.size();
		send_buffer.put_u8(id);
		send_buffer.put_u32(msg_len);
	
	send_buffer.put_data(packet)
	

func _send_heartbeat():
	if not is_connected:
		return
	print("_send_heartbeat")
	check_send_count = 0
	pre_send_time = Time.get_unix_time_from_system()
	send_buffer.put_u8(EGammaConnectsID.eGC_HeartbeatMsg)
	_flush_send_buffer()

func _send_heartbeart_reply():
	send_buffer.put_8(EGammaConnectsID.eGC_HeartbeatReply)
	_flush_send_buffer()
	
func _on_heartbeat():
	_send_heartbeart_reply()
	
func _on_heartbeat_reply():
	ping_delay = Time.get_unix_time_from_system() - pre_send_time
	pre_send_time = 0

func _on_heartbeart_stop():
	print("HeartBeatStop ")
	socket.close()
	is_connected = false


func _check_time_out():
	if not is_connected:
		return
	check_send_count = check_send_count + 1
	check_recv_count = check_recv_count + 1
	if check_send_count > heart_beart_interval and pre_send_time == 0:
		_send_heartbeat()
	
	if auto_disconnected_frame < check_recv_count:
		_on_heartbeart_stop()

func  _on_ShellMsg8(bytes : PackedByteArray):
	pass

func _on_SheddMsg32(bytes : PackedByteArray):
	pass
	
func _flush_send_buffer():
	var send_err = socket.send(send_buffer.data_array, WebSocketPeer.WRITE_MODE_BINARY)
	if send_err == OK:
		send_buffer.clear()
		send_buffer.seek(0)
	else:
		printerr("socket_send error")
	
func send_bin(packet: PackedByteArray):
	_encode_binary_packet(packet)
	_flush_send_buffer()
	
func send_proto():
	pass
